
goog.provide('esn.dao.BaseDao');

goog.require('goog.Timer');
goog.require('goog.array');


/**
 * @constructor
 */
esn.dao.BaseDao = function(opt_throttleTime) {
  this.isSaving_ = false;

  this.isDeleted_ = false;

  this.savePending_ = false;

  this.saveCallbackQueue_ = [];

  this.throttleTime_ = opt_throttleTime || null;

  this.trottleActive_ = false;
};

esn.dao.BaseDao.prototype.isSaving = function() {
  return this.isSaving_;
};

esn.dao.BaseDao.prototype.isDeleted = function() {
  return this.isDeleted_;
};

esn.dao.BaseDao.prototype.save = function(opt_callback) {
  if (this.isDeleted_) {
    throw new Error('This model has been deleted.');
  }

  if (this.isSaving_ || this.trottleActive_) {
    // We're still processing a previous save so put this callback into a queue
    // to be called after on the "next" save call.
    this.savePending_ = true;
    if (opt_callback) {
      this.saveCallbackQueue_.push(opt_callback);
    }
  } else {
    // This is the first call so let's create a callback list with, at most,
    // one item.
    var callbacks = opt_callback ? [opt_callback] : [];
    this.save_(callbacks);
  }
}

esn.dao.BaseDao.prototype.save_ = function(callbacks) {
  this.isSaving_ = true;

  // If the throttle time is set let's enable it to prevent too many save calls.
  if (this.throttleTime_) {
    this.trottleActive_ = true;
    goog.Timer.callOnce(
        goog.bind(this.handleThrottleCheck_, this),
        this.throttleTime_,
        this);
  }

  this.saveInternal(goog.bind(this.saveResponse_, this, callbacks));
};

esn.dao.BaseDao.prototype.saveResponse_ = function(callbacks, response) {
  // Note: We dont' want to turn off the "saving" flag until after our callback
  // functions have been called (to make sure we queue up any additional saves).
  goog.array.forEach(callbacks, function(callback) {
    callback(response);
  });

  this.isSaving_ = false;

  // If this model hasn't been deleted and there is a pending save action let's
  // do it now.
  if (!this.isDeleted_ && this.savePending_ && !this.trottleActive_) {
    var callbacks = this.saveCallbackQueue_;
    this.saveCallbackQueue_ = [];
    this.savePending_ = false;
    this.save_(callbacks);
  }
};

esn.dao.BaseDao.prototype.handleThrottleCheck_ = function() {
  this.trottleActive_ = false;
  // Call the {@code saveResponse_} method to trigger any pending saves.
  this.saveResponse_([], {});
};

/**
 */
esn.dao.BaseDao.prototype.remove = function(opt_callback) {
  if (this.isDeleted_) {
    throw new Error('This model has been deleted.');
  }

  // Mark this object as deleted to prevent further save & delete actions.
  this.isDeleted_ = true;

  // If there's a pending save clear it. No need to make any more updates
  // since this model is being deleted.
  this.savePending_ = false;
  this.saveCallbackQueue_ = [];

  this.removeInternal(
      goog.bind(this.removeResponse_, this, opt_callback || null));
};

esn.dao.BaseDao.prototype.removeResponse_ = function(callback, response) {
  if (callback) {
    callback(response);
  }
};

esn.dao.BaseDao.prototype.getModel = function() {
  goog.abstractMethod();
};

/**
 * @protected
 */
esn.dao.BaseDao.prototype.saveInternal = function(callback) {
  goog.abstractMethod();
};

/**
 * @protected
 */
esn.dao.BaseDao.prototype.removeInternal = function(callback) {
  goog.abstractMethod();
};
